package com.pig4cloud.pig.common.core.util;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Security;
import java.util.Base64;

/**
 * @Description
 * @Author chengang
 * @Date 2022/12/13
 */
public class AESUtils {

	static {
		Security.addProvider(new BouncyCastleProvider());
	}

	/**
	 * 将字符串 keyString 的AES秘钥转换成SecretKey对象
	 *
	 * @param keyString
	 * @return
	 * @throws Exception
	 */
	public static SecretKey loadKeyAES(String keyString) throws Exception {
		return new SecretKeySpec(keyString.getBytes(StandardCharsets.UTF_8), "AES");
	}

	/**
	 * 加密
	 *
	 * @param source
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static String encryptAES(byte[] source, SecretKey key, String vectorKey) throws Exception {
		Security.addProvider(new BouncyCastleProvider());
		IvParameterSpec iv = new IvParameterSpec(vectorKey.getBytes(StandardCharsets.UTF_8));
		int dataLength = source.length;
		if (dataLength % 16 != 0) {
			dataLength = dataLength + (16 - (dataLength % 16));
		}
		byte[] paddingBytes = new byte[dataLength];
		System.arraycopy(source, 0, paddingBytes, 0, source.length);
		Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
		cipher.init(Cipher.ENCRYPT_MODE, key, iv);
		byte[] encryped = cipher.doFinal(paddingBytes);
		return bin2HexStr(encryped);
	}

	/**
	 * 加密
	 *
	 * @param source
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static String encryptAES(byte[] source, SecretKey key) throws Exception {
		Security.addProvider(new BouncyCastleProvider());
		int dataLength = source.length;
		if (dataLength % 16 != 0) {
			dataLength = dataLength + (16 - (dataLength % 16));
		}
		byte[] paddingBytes = new byte[dataLength];
		System.arraycopy(source, 0, paddingBytes, 0, source.length);
		Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
		cipher.init(Cipher.ENCRYPT_MODE, key);
		byte[] encryped = cipher.doFinal(paddingBytes);
		return Base64.getEncoder().encodeToString(encryped);
	}

	/**
	 * 解密
	 *
	 * @param encrypedStr
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static String decryptAES(String encrypedStr, SecretKey key) throws Exception {
		Security.addProvider(new BouncyCastleProvider());
		byte[] source = Base64.getDecoder().decode(encrypedStr);
		Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
		cipher.init(Cipher.DECRYPT_MODE, key);
		byte[] retBytes = cipher.doFinal(source);
		return new String(retBytes, StandardCharsets.UTF_8);
	}

	/**
	 * 解密
	 *
	 * @param encrypedStr
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static String decryptAES(String encrypedStr, SecretKey key, String vectorKey) throws Exception {
		Security.addProvider(new BouncyCastleProvider());
		IvParameterSpec iv = new IvParameterSpec(vectorKey.getBytes());
		byte[] source = hexStringToByte(encrypedStr);
		Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
		cipher.init(Cipher.DECRYPT_MODE, key, iv);
		byte[] retBytes = cipher.doFinal(source);
		return new String(retBytes, StandardCharsets.UTF_8);
	}

	/**
	 * @param bytes
	 * @return 将二进制数组转换为十六进制字符串  2-16
	 */
	public static String bin2HexStr(byte[] bytes) {
		String hexStr = "0123456789ABCDEF";
		StringBuilder result = new StringBuilder();
		String hex = "";
		for (int i = 0; i < bytes.length; i++) {
			// 字节高4位
			hex = String.valueOf(hexStr.charAt((bytes[i] & 0xF0) >> 4));
			// 字节低4位
			hex += String.valueOf(hexStr.charAt(bytes[i] & 0x0F));
			result.append(hex);
		}
		return result.toString();
	}

	private static int parse(char c) {
		if (c >= 'a') {
			return (c - 'a' + 10) & 0x0f;
		}
		if (c >= 'A') {
			return (c - 'A' + 10) & 0x0f;
		}
		return (c - '0') & 0x0f;
	}

	public static byte[] hexStringToByte(String hex) {
		byte[] b = new byte[hex.length() / 2];
		int j = 0;
		for (int i = 0; i < b.length; i++) {
			char c0 = hex.charAt(j++);
			char c1 = hex.charAt(j++);
			b[i] = (byte) ((parse(c0) << 4) | parse(c1));
		}
		return b;
	}


}
